package com.ego.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 袁梦达
 */
public class ApplicationContext {


    //配置文件  扫描包
    private Class configClass;
    //bean工厂
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    //单例bean
    private ConcurrentHashMap<String, Object> singletonMap = new ConcurrentHashMap<>();
    //后处理器
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    public ApplicationContext(Class configClass){
        this.configClass = configClass;

        //扫描
        if(configClass.isAnnotationPresent(ComponentScan.class)){

            //获取ComponentScan的值
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);

            //com.ego.service
            String scanPath = componentScanAnnotation.value();
            //com/ego/service
            scanPath = scanPath.replace(".", "/");
            //去out文件夹里找到扫描的包
            ClassLoader classLoader = ApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(scanPath);

            //扫描包里每一个class文件是否为Bean
            File file = new File(resource.getFile());
            if(file.isDirectory()){
                for (File f : file.listFiles()) {
                    String fileName = f.getAbsolutePath();
                    if(fileName.endsWith(".class")){
                        String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                        className = className.replace("\\", ".");

                        try {

                            Class<?> clazz = classLoader.loadClass(className);
                            //判断是否为Bean
                            if(clazz.isAnnotationPresent(Component.class)){

                                //如果是后处理器则加入list中
                                if(BeanPostProcessor.class.isAssignableFrom(clazz)){
                                    BeanPostProcessor instance = (BeanPostProcessor) clazz.getConstructor().newInstance();
                                    beanPostProcessorList.add(instance);
                                }

                                Component componentAnnotation = clazz.getAnnotation(Component.class);
                                String beanName = componentAnnotation.value();

                                //默认Bean的name
                                if("".equals(beanName)){
                                    beanName = Introspector.decapitalize(clazz.getSimpleName());
                                }

                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setClazz(clazz);

                                //作用域
                                if(clazz.isAnnotationPresent(Scope.class)){
                                    Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
                                    beanDefinition.setScope(scopeAnnotation.value());
                                }else{
                                    beanDefinition.setScope("singleton");
                                }
                                beanDefinitionMap.put(beanName, beanDefinition);
                            }

                        } catch (ClassNotFoundException | InvocationTargetException | InstantiationException |
                                 IllegalAccessException | NoSuchMethodException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }

            }
        }

        //将所有单例Bean加入到单例池
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if("singleton".equals(beanDefinition.getScope())){
                Object bean = createBean(beanName, beanDefinition);
                singletonMap.put(beanName, bean);
            }
        }


    }

    private Object createBean(String beanName, BeanDefinition beanDefinition){
        Class clazz = beanDefinition.getClazz();
        Object bean = null;
        try {
            //实例化
            bean = clazz.getConstructor().newInstance();

            //依赖注入
            for (Field field : clazz.getFields()) {
                if(field.isAnnotationPresent(Autowired.class)){
                    field.setAccessible(true);
                    field.set(bean, getBean(field.getName()));
                }
            }

            //执行回调接口
            if(bean instanceof BeanNameAware){
                ((BeanNameAware)bean).setBeanName(beanName);
            }

            //前置处理器
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.postProcessorBeforeInitialization(beanName, bean);
            }

            //初始化
            if(bean instanceof InitializingBean){
                ((InitializingBean)bean).afterPropertiesSet();
            }

            //后置处理器
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.postProcessorAfterInitialization(beanName, bean);
            }

        } catch (InstantiationException | InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
        return bean;
    }
    public Object getBean(String beanName){
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        if(beanDefinition == null){
            throw new NullPointerException("bean不存在");
        }else {
            String scope = beanDefinition.getScope();
            if("singleton".equals(scope)){
                //单例Bean直接从单例池中获取
                Object bean = singletonMap.get(beanName);

                //此条件发生在初始化容器时，生成单例Bean的时候
                //在某个Bean依赖注入时，如果被注入的单例Bean还没有实例化，
                //那么就要先实例化被注入的单例Bean，再实例化当前Bean
                if(bean == null){
                    bean= createBean(beanName, beanDefinition);
                }
                return bean;
            }else{
                return createBean(beanName, beanDefinition);
            }
        }
    }
}
